package drds.server.config;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import drds.server.config.cluster.Cluster;
import drds.server.config.loader.Config;
import drds.server.config.loader.SchemaLoader;
import drds.server.config.loader.xml.XmlConfig;
import drds.server.config.loader.xml.XmlSchemaLoader;
import drds.server.config.model.DRDS;
import drds.server.config.model.GroupDataSourceConfig;
import drds.server.config.model.GroupDataSourceNodeConfig;
import drds.server.config.model.MysqlInstanceInfo;
import drds.server.config.model.Schema;
import drds.server.config.model.User;
import drds.server.config.util.ConfigException;
import drds.server.datasource.DataSource;
import drds.server.datasource.GroupDataSource;
import drds.server.datasource.GroupDataSourceNode;
import drds.server.route.sequence.SequenceHandlerImpl;

public class ConfigInitializer {

	private volatile DRDS drds;
	private volatile Cluster cluster;
	private volatile Map<String, User> userMap;
	private volatile Schema schema;

	public Schema getSchema() {
		return schema;
	}

	public void setSchema(Schema schema) {
		this.schema = schema;
	}

	private volatile Map<String, GroupDataSourceNode> groupDataSourceNodeMap;
	private volatile Map<String, GroupDataSource> groupDataSourceMap;

	public ConfigInitializer(boolean loadDataHost) {

		// 读取rule.xml和schema.xml
		SchemaLoader schemaLoader = new XmlSchemaLoader();

		// 读取server.xml
		XmlConfig xmlConfig = new XmlConfig(schemaLoader);

		schemaLoader = null;

		// 加载配置
		this.drds = xmlConfig.getSystemConfig();
		this.userMap = xmlConfig.getUserMap();

		// 是否重新加载DataHost和对应的DataNode
		if (loadDataHost) {
			this.groupDataSourceMap = initGroupDataSourceMap(xmlConfig);
			this.groupDataSourceNodeMap = initGroupDataSourceNodeMap(xmlConfig);
		}

		this.cluster = initCluster(xmlConfig);

		// 不同类型的全局序列处理器的配置加载
		if (drds.getSequnceHandlerType() == DRDS.SEQUENCEHANDLER_MYSQLDB) {
			SequenceHandlerImpl.getInstance().load();
		}

		/**
		 * 配置文件初始化， 自检
		 */
		this.selfChecking0();
	}

	private void selfChecking0() throws ConfigException {

		// 检查user与schema配置对应以及schema配置不为空
		if (userMap == null || userMap.isEmpty()) {
			throw new ConfigException("SelfCheck### user all node is empty!");

		} else {

			for (User user : userMap.values()) {
				if (user == null) {
					throw new ConfigException("SelfCheck### users node within the item is empty!");
				}

			}
		}

		// schema 配置检测
		if (null == schema) {
			throw new ConfigException("SelfCheck### schema all node is empty!");

		} else {
			// check dataNode / dataHost 节点
			if (this.groupDataSourceNodeMap != null && this.groupDataSourceMap != null) {
				Set<String> dataNodeNameSet = schema.getAllDataNodes();
				for (String dataNodeName : dataNodeNameSet) {

					GroupDataSourceNode groupDataSourceNode = this.groupDataSourceNodeMap.get(dataNodeName);
					if (groupDataSourceNode == null) {
						throw new ConfigException("SelfCheck### schema dbnode is empty!");
					}
				}
			}
		}

	}

	public void testConnection() {

		// 实际链路的连接测试
		if (this.groupDataSourceNodeMap != null && this.groupDataSourceMap != null) {

			Map<String, Boolean> map = new HashMap<String, Boolean>();

			for (GroupDataSourceNode groupDataSourceNode : groupDataSourceNodeMap.values()) {

				String database = groupDataSourceNode.getSchemaName();
				GroupDataSource groupDataSource = groupDataSourceNode.getGroupDataSource();

				for (DataSource dataSource : groupDataSource.getDataSourceList()) {
					String key = dataSource.getId() + "_" + database;
					if (map.get(key) == null) {
						map.put(key, false);

						boolean isConnected = false;
						try {
							isConnected = dataSource.testConnection();
							map.put(key, isConnected);
						} catch (IOException e) {
						}
					}
				}
			}

			//
			boolean isConnectivity = true;
			for (Map.Entry<String, Boolean> entry : map.entrySet()) {
				Boolean value = entry.getValue();
				if (!value && isConnectivity) {
					isConnectivity = false;

				} else {
				}
			}

			if (!isConnectivity) {
				throw new ConfigException("SelfCheck### there are some datasource connection failed, pls check!");
			}

		}

	}

	public DRDS getSystemConfig() {
		return drds;
	}

	public Cluster getCluster() {
		return cluster;
	}

	public Map<String, User> getUserMap() {
		return userMap;
	}

	public Map<String, GroupDataSourceNode> getGroupDataSourceNodeMap() {
		return groupDataSourceNodeMap;
	}

	public Map<String, GroupDataSource> getDataHosts() {
		return this.groupDataSourceMap;
	}

	private Cluster initCluster(Config configLoader) {
		return new Cluster(configLoader.getClusterConfig());
	}

	private Map<String, GroupDataSource> initGroupDataSourceMap(Config configLoader) {
		Map<String, GroupDataSourceConfig> groupDataSourceConfigMap = configLoader.getGroupDataSourceConfigMap();
		// 根据DataHost建立PhysicalDBPool，其实就是实际数据库连接池，每个DataHost对应一个PhysicalDBPool
		Map<String, GroupDataSource> groupDataSourceMap = new HashMap<String, GroupDataSource>(groupDataSourceConfigMap.size());
		for (GroupDataSourceConfig groupDataSourceConfig : groupDataSourceConfigMap.values()) {
			// 建立PhysicalDBPool
			GroupDataSource groupDataSource = getGroupDataSource(groupDataSourceConfig, configLoader);
			groupDataSourceMap.put(groupDataSource.getHostName(), groupDataSource);
		}
		return groupDataSourceMap;
	}

	private DataSource[] createDataSources(GroupDataSourceConfig groupDataSourceConfig, String hostName, String dbType, String dbDriver, MysqlInstanceInfo[] hosts, boolean isRead) {
		DataSource[] dataSources = new DataSource[hosts.length];
		for (int i = 0; i < hosts.length; i++) {
			MysqlInstanceInfo host = null;
			DataSource dataSource = new DataSource(host, groupDataSourceConfig, isRead);
			dataSources[i] = dataSource;
		}
		return dataSources;
	}

	private GroupDataSource getGroupDataSource(GroupDataSourceConfig groupDataSourceConfig, Config configLoader) {
		String name = groupDataSourceConfig.getName();

		// 针对所有写节点创建PhysicalDatasource
		DataSource[] writeDataSources = createDataSources(groupDataSourceConfig, name, "", "", groupDataSourceConfig.getWriteHosts(), false);
		Map<Integer, MysqlInstanceInfo[]> readHostsMap = groupDataSourceConfig.getReadHostsMap();
		Map<Integer, DataSource[]> readDataSourceMap = new HashMap<Integer, DataSource[]>(readHostsMap.size());
		// 对于每个读节点建立key为writeHost下标value为readHost的PhysicalDatasource[]的哈希表
		for (Map.Entry<Integer, MysqlInstanceInfo[]> entry : readHostsMap.entrySet()) {
			DataSource[] readDataSources = createDataSources(groupDataSourceConfig, name, "", "", entry.getValue(), true);
			readDataSourceMap.put(entry.getKey(), readDataSources);
		}
		GroupDataSource groupDataSource = new GroupDataSource(groupDataSourceConfig.getName(), groupDataSourceConfig, writeDataSources, readDataSourceMap, groupDataSourceConfig.getBalanceType());
		return groupDataSource;
	}

	private Map<String, GroupDataSourceNode> initGroupDataSourceNodeMap(Config configLoader) {
		Map<String, GroupDataSourceNodeConfig> groupDataSourceNodeConfigMap = configLoader.getGroupDataSourceNodeConfigMap();
		Map<String, GroupDataSourceNode> groupDataSourceNodeMap = new HashMap<String, GroupDataSourceNode>(groupDataSourceNodeConfigMap.size());
		for (GroupDataSourceNodeConfig groupDataSourceNodeConfig : groupDataSourceNodeConfigMap.values()) {
			GroupDataSource groupDataSource = this.groupDataSourceMap.get(groupDataSourceNodeConfig.getDataHost());
			if (groupDataSource == null) {
				throw new ConfigException("dataHost not exists " + groupDataSourceNodeConfig.getDataHost());

			}
			GroupDataSourceNode groupDataSourceNode = new GroupDataSourceNode(groupDataSourceNodeConfig.getName(), groupDataSourceNodeConfig.getDatabase(), groupDataSource);
			groupDataSourceNodeMap.put(groupDataSourceNode.getHostName(), groupDataSourceNode);
		}
		return groupDataSourceNodeMap;
	}

}